import * as fs from 'fs';
import * as http from 'http';
import * as path from 'path';
import config from '../config'
import { BaseInfo, FetchAPI, FetchResult, UploadInfo } from '../interfaces';
import db from "../db"
import { ERRORS, getBaseInfo, logger } from '../utils/common'
import { ObjectId } from 'mongodb';
import createRespUtil from 'f2e-server/lib/util/resp'
import mime from 'mime'
import { F2EConfig } from 'f2e-server';

const cacheRoot = path.resolve(process.cwd(), config.cacheRoot)
if (!fs.existsSync(cacheRoot)) {
  logger.log('Error', 'cacheRoot not exist!', cacheRoot)
  fs.mkdir(cacheRoot, function (err) {
    if (err) {
      console.error(err)
      process.exit(1)
    }
  })
}

export const begin_upload: FetchAPI<UploadInfo, UploadInfo & BaseInfo & { loaded: number }> = async (info, options) => {
  if (!options || !info) return {
    error: ERRORS.PARAMS
  }
  const conn = await db.getCollection('upload')
  let find = await conn.findOne({ md5: info.md5, filename: info.filename })
  const baseInfo = getBaseInfo(options.loginUser, find)
  if (find) {
    Object.assign(find, baseInfo)
    await conn.updateOne({ _id: find._id }, { $set: baseInfo })
  } else {
    const { insertedId } = await conn.insertOne({ ...info, ...baseInfo })
    find = {
      _id: insertedId,
      ...info, ...baseInfo,
    }
  }
  const cache_path = path.join(cacheRoot, info.md5)
  let loaded = 0
  if (fs.existsSync(cache_path)) {
    const cache_stat = fs.statSync(cache_path)
    if (cache_stat.isFile()) {
      loaded = cache_stat.size
    }
  }
  return {
    success: true,
    data: {
      ...find,
      loaded,
    },
  }
}

export const download = async (req: http.IncomingMessage, resp: http.ServerResponse, conf: F2EConfig) => {
  const { handleNotFound } = createRespUtil(conf)
  const { searchParams, pathname } = new URL(req.url + '', 'http://127.0.0.1')
  const id = searchParams.get('id')
  if (!id) {
    return handleNotFound(req, resp, pathname)
  }
  const conn = await db.getCollection('upload')
  const find = await conn.findOne({ _id: new ObjectId(id)})
  if (!find) {
    return handleNotFound(req, resp, pathname)
  }
  if (find) {
    const cache_path = path.join(cacheRoot, find?.md5)
    const contentType = mime.getType(pathname)
    if (fs.existsSync(cache_path)) {
      resp.writeHead(200, {
        'Content-Type': contentType,
        'content-Disposition': 'attachement; filename=' + encodeURIComponent(find.filename),
      })
      fs.createReadStream(cache_path).pipe(resp)
    } else {
      return handleNotFound(req, resp, pathname)
    }
  }
}

export const do_upload: FetchAPI = async ({}, ctx) => {
  if (!ctx || !ctx.req) return {
    error: ERRORS.PARAMS
  }
  const { req } = ctx
  const { searchParams } = new URL(req.url + '', 'http://127.0.0.1')
  const md5 = searchParams.get('md5')
  if (!md5) {
    return {
      error: ERRORS.PARAMS
    }
  }
  const cache_path = path.join(cacheRoot, md5)
  let size = 0
  if (fs.existsSync(cache_path)) {
    const cache_stat = fs.statSync(cache_path)
    if (cache_stat.isFile()) {
      size = cache_stat.size
    }
  }
  return new Promise<FetchResult<unknown>>(function (resolve) {
    req.on('data', data => {
      size += data.length
      if (size > config.maxUploadSize * 1024) {
        resolve({
          error: ERRORS.OUTOFSIZE
        })
        return
      }
      if (size > data.length) {
        fs.appendFileSync(cache_path, data)
      } else {
        fs.writeFileSync(cache_path, data)
      }
    }).on('end', function () {
      resolve({
        success: true,
      })
    }).on('error', function (err) {
      logger.log('Error', err)
      resolve({
        error: ERRORS.UNKNOWN
      })
    })
  })
}
